#pragma once

#include<iostream>
#include<string>
// #include<cstdlib>
#include"Log.hpp"
// #include <unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <cstring>
#include<pthread.h>
#include"ThreadPool.hpp"
#include"Daemon.hpp"
#include "Task.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;// 但是一般不要设置的太大?

extern Log lg;

enum{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};

class TcpServer;

class ThreadData{
public:
    ThreadData(int fd,const std::string &ip,const uint16_t &port, TcpServer *t)
    :sockfd(fd),clientip(ip),clientport(port),tsvr(t){}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

class TcpServer{
public:
    TcpServer(const uint16_t &port, const std::string &ip =defaultip)
    :listensock_(defaultfd),port_(port),ip_(ip){}
    
    // 
    void InitServer(){
        listensock_ = socket(AF_INET,SOCK_STREAM,0);//
        if(listensock_<0){
            lg(Fatal,"create socket,errno: %d,errstring: %s",errno,strerror(errno));
            exit(SocketError);
        }
        lg(Info,"create socket success, listensock_: %d",listensock_);
        
        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));
        //SO_REUSEADDR: 允许在TCP断开连接后,立即重新绑定断连的地址
        //SO_REUSEPORT: 允许多个套接字绑定一个端口
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_port = htons(port_);
        local.sin_family = AF_INET;
        inet_aton(ip_.c_str(),&(local.sin_addr));
        
        if(bind(listensock_,(struct sockaddr *)&local,sizeof(local))<0){
            lg(Fatal,"bind error, error: %d,errstring: %s",errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind socket success, listensock_: %d",listensock_);
        
        //TCP是面向连接的,服务器一般死比较被动的,服务器一直处于一种,一直在等待连接的到来的状态
        if(listen(listensock_,backlog)<0){
            lg(Fatal, "listen error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info,"listen socket success, listensock_: %d",listensock_);
    }
    
    void start(){
        // Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info, "tcpServer is runing ......");
        while(true){
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_,(struct sockaddr *)&client,&len);
            if(sockfd<0){
                lg(Warning, "accept error, errno: %d,errstirng: %s",errno,strerror(errno));
                continue;
            }
        
            uint16_t clientport = ntohs(client.sin_port);//网络转主机net->host
            char clientip[32];
            inet_ntop(AF_INET,&(client.sin_addr),clientip,sizeof(clientip));

            lg(Info,"get a new link..., sockfd: %d, client ip: %s, client port: %d",sockfd,clientip,clientport);
            
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
        
private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};

